TestRouter   A
last analyzed

Complexity

Total Complexity 1

Size/Duplication

Total Lines 6
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 1
eloc 4
dl 0
loc 6
rs 10
c 0
b 0
f 0

1 Function

Rating   Name   Duplication   Size   Complexity  
A test 0 3 1
1
import { flushPromises } from '@test/utils/testUtils';
2
import * as http from 'http';
3
import { NextFunction, Request, Response } from 'express';
4
import request from 'supertest';
5
import ExpressBeans from '@/core/ExpressBeans';
6
import { Logger, Shutdown, Route, RouterBean } from '@/main';
7
import { logger } from '@/core';
8
import { Executor } from '@/core/Executor';
9
10
jest.mock('pino-http', () => ({
11
  pinoHttp: ({
12
    logger: loggerInstance,
13
    customSuccessMessage,
14
    customErrorMessage,
15
  }: {
16
    logger: Logger,
17
    customSuccessMessage: (req: Request, res: Response) => string,
18
    customErrorMessage: (req: Request, res: Response) => string
19
  }) => (req: Request, res: Response, next: NextFunction) => {
20
    if (res.err) {
21
      loggerInstance.error(customErrorMessage(req, res));
22
    } else {
23
      loggerInstance.info(customSuccessMessage(req, res));
24
    }
25
    next();
26
  },
27
  startTime: jest.requireActual('pino-http').startTime,
28
}));
29
jest.mock('@/core', () => ({
30
  registeredBeans: new Map(),
31
  registeredMethods: new Map(),
32
  logger: {
33
    info: jest.fn(),
34
    debug: jest.fn(),
35
    error: jest.fn(),
36
  },
37
}));
38
39
describe('ExpressBeans integration tests', () => {
40
  let server: http.Server;
41
  let server2: http.Server;
42
  let application: ExpressBeans;
43
  let application2: ExpressBeans;
44
  beforeEach(() => {
45
    jest.resetModules();
46
    jest.clearAllMocks();
47
    Executor.stopLifecycle();
48
  });
49
50
  afterEach(() => {
51
    server.close();
52
    if (server2) {
53
      server2.close();
54
    }
55
  });
56
57
  test('start of application', async () => {
58
    // GIVEN
59
    @RouterBean('/test')
60
    class TestRouter {
61
      @Route('GET', '/42')
62
      test(_req: Request, res: Response) {
63
        res.send('42 is the answer');
64
      }
65
    }
66
    application = new ExpressBeans({ listen: false, routerBeans: [TestRouter] });
67
    await flushPromises();
68
    server = application.listen(3001);
69
    await flushPromises();
70
71
    // WHEN
72
    const { text } = await request(server).get('/test/42').expect(200);
73
74
    // THEN
75
    expect(text).toBe('42 is the answer');
76
  });
77
78
  test('start of application with baseURL', async () => {
79
    // GIVEN
80
    @RouterBean('/test')
81
    class TestRouter {
82
      @Route('GET', '/42')
83
      test(_req: Request, res: Response) {
84
        res.send('42 is the answer');
85
      }
86
    }
87
    application = new ExpressBeans({ listen: false, routerBeans: [TestRouter], baseURL: '/api' });
88
    await flushPromises();
89
    server = application.listen(3001);
90
    await flushPromises();
91
92
    // WHEN
93
    const { text } = await request(server).get('/api/test/42').expect(200);
94
95
    // THEN
96
    expect(text).toBe('42 is the answer');
97
  });
98
99
  test('creation of a new application', async () => {
100
    // WHEN
101
    application = new ExpressBeans({ listen: false });
102
    server = application.getApp().listen(3000);
103
    await flushPromises();
104
105
    // THEN
106
    expect(application instanceof ExpressBeans).toBe(true);
107
  });
108
109
  test('start multiple applications', async () => {
110
    // GIVEN
111
    @RouterBean('/test1')
112
    class TestRouter1 {
113
      @Route('GET', '/42')
114
      test(_req: Request, res: Response) {
115
        res.send('42 is the answer');
116
      }
117
    }
118
    @RouterBean('/test2')
119
    class TestRouter2 {
120
      @Route('GET', '/thanks')
121
      test(_req: Request, res: Response) {
122
        res.send('for all the fish');
123
      }
124
    }
125
    application = new ExpressBeans({ listen: false, routerBeans: [TestRouter1] });
126
    application2 = new ExpressBeans({ listen: false, routerBeans: [TestRouter2] });
127
    await flushPromises();
128
    server = application.listen(4001);
129
    server2 = application2.listen(4002);
130
    await flushPromises();
131
    await Executor.getExecutionPhase('init');
132
    await flushPromises();
133
134
    // WHEN
135
    const { text } = await request(server).get('/test1/42').expect(200);
136
    const { text: text2 } = await request(server2).get('/test2/thanks').expect(200);
137
    await request(server).get('/test2/thanks').expect(404);
138
    await request(server2).get('/test1/42').expect(404);
139
140
    // THEN
141
    expect(text).toBe('42 is the answer');
142
    expect(text2).toBe('for all the fish');
143
  });
144
145
  test('logs incoming requests', async () => {
146
    // GIVEN
147
    const loggerMock = jest.mocked(logger);
148
    @RouterBean('/test')
149
    class TestRouter {
150
      @Route('GET', '/42')
151
      test(_req: Request, res: Response) {
152
        res.send('42 is the answer');
153
      }
154
    }
155
    application = new ExpressBeans({ listen: false, routerBeans: [TestRouter] });
156
    await flushPromises();
157
    await Executor.getExecutionPhase('init');
158
    server = application.listen(3001);
159
    await flushPromises();
160
161
    // WHEN
162
    await (await request(server).get('/test/42').expect(200));
163
164
    // THEN
165
    expect(loggerMock.info).toHaveBeenCalledWith('::ffff:127.0.0.1 - "GET /test/42 HTTP/1.1" 200 - NaNms');
166
  });
167
168
  test('logs incoming requests using ip from header (proxy)', async () => {
169
    // GIVEN
170
    const loggerMock = jest.mocked(logger);
171
    @RouterBean('/test')
172
    class TestRouter {
173
      @Route('GET', '/42')
174
      test(_req: Request, res: Response) {
175
        res.send('42 is the answer');
176
      }
177
    }
178
    application = new ExpressBeans({ listen: false, routerBeans: [TestRouter] });
179
    await flushPromises();
180
    server = application.listen(3001);
181
    await Executor.getExecutionPhase('init');
182
    await flushPromises();
183
184
    // WHEN
185
    await (await request(server).get('/test/42').set({ 'x-forwarded-for': '193.234.61.32' }).expect(200));
186
187
    // THEN
188
    expect(loggerMock.info).toHaveBeenCalledWith('193.234.61.32 - "GET /test/42 HTTP/1.1" 200 - NaNms');
189
  });
190
191
  test('Shutdown hooks on process exit', async () => {
192
    // GIVEN
193
    const mockExit = jest.spyOn(process, 'exit')
194
      .mockImplementationOnce((code?: string | number | null) => process.emit('beforeExit', Number(code)) as never);
195
    const mock = jest.fn();
196
197
198
    @RouterBean('/test')
199
    class TestBean {
200
201
      @Shutdown
202
      exit() {
203
        mock();
204
      }
205
    }
206
207
    // WHEN
208
    application = new ExpressBeans({ listen: false, routerBeans: [TestBean] });
209
    server = application.listen(3001);
210
    await flushPromises();
211
    await Executor.getExecutionPhase('init');
212
    process.exit(0);
213
214
    // THEN
215
    expect(mock).toHaveBeenCalled();
216
    expect(mockExit).toHaveBeenCalledWith(0);
217
    mockExit.mockRestore();
218
  });
219
220
  test('Shutdown hook not called on normal execution', async () => {
221
    // GIVEN
222
    const mock = jest.fn();
223
224
    @RouterBean('/test')
225
    class TestBean {
226
227
      @Shutdown
228
      exit() {
229
        mock();
230
      }
231
    }
232
233
    // WHEN
234
    application = new ExpressBeans({ listen: false, routerBeans: [TestBean] });
235
    server = application.listen(3001);
236
    await flushPromises();
237
    await Executor.getExecutionPhase('init');
238
239
    // THEN
240
    expect(mock).not.toHaveBeenCalled();
241
  });
242
});
243